package com.mlethe.library.fingerprint;

import android.content.Context;
import android.content.DialogInterface;
import android.hardware.biometrics.BiometricPrompt;
import android.os.Build;
import android.os.CancellationSignal;
import android.text.TextUtils;

import androidx.annotation.RequiresApi;
import androidx.core.hardware.fingerprint.FingerprintManagerCompat;

import com.mlethe.library.fingerprint.callback.FingerprintCallback;
import com.mlethe.library.fingerprint.callback.IFingerprint;
import com.mlethe.library.fingerprint.entity.VerificationDialogStyle;
import com.mlethe.library.fingerprint.uitls.CipherHelper;

import java.util.concurrent.Executor;

/**
 * Android P == 9.0
 *
 * @author Mlethe
 * @date 2019/7/9
 */
@RequiresApi(api = Build.VERSION_CODES.P)
class FingerprintImplForAndrP implements IFingerprint {

    /**
     * 指纹加密
     */
    private BiometricPrompt.CryptoObject cryptoObject;
    /**
     * 用于取消扫描器的扫描动作
     */
    private CancellationSignal cancellationSignal;
    /**
     * 指向调用者的指纹回调
     */
    private FingerprintCallback fingerprintCallback;
    /**
     * 弹窗样式
     */
    private VerificationDialogStyle verificationDialogStyle;

    /**
     * 认证结果回调
     */
    private BiometricPrompt.AuthenticationCallback authenticationCallback = new BiometricPrompt.AuthenticationCallback() {
        @Override
        public void onAuthenticationError(int errorCode, CharSequence errString) {
            super.onAuthenticationError(errorCode, errString);
            if (errorCode == 5) {
                // 用户取消指纹验证，不必向用户抛提示信息
                if (fingerprintCallback != null) {
                    fingerprintCallback.onCancel();
                }
                onDestroy();
            } else {
                if (fingerprintCallback != null) {
                    fingerprintCallback.onError(errString.toString());
                }
            }
        }

        @Override
        public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
            super.onAuthenticationHelp(helpCode, helpString);
            if (fingerprintCallback != null) {
                fingerprintCallback.onError(helpString.toString());
            }
        }

        @Override
        public void onAuthenticationSucceeded(BiometricPrompt.AuthenticationResult result) {
            super.onAuthenticationSucceeded(result);
            if (fingerprintCallback != null) {
                fingerprintCallback.onSucceeded();
            }
            onDestroy();
        }

        @Override
        public void onAuthenticationFailed() {
            super.onAuthenticationFailed();
            if (fingerprintCallback != null) {
                fingerprintCallback.onFailed();
            }
            onDestroy();
        }
    };

    private static final class Holder {
        private static final FingerprintImplForAndrP INSTANCE = new FingerprintImplForAndrP();
    }

    private FingerprintImplForAndrP() {
    }

    public static FingerprintImplForAndrP getInstance() {
        FingerprintImplForAndrP instance = Holder.INSTANCE;
        instance.createCryptoObject();
        return instance;
    }

    /**
     * 创建指纹加密
     */
    private void createCryptoObject() {
        // 指纹加密，提前进行Cipher初始化，防止指纹认证时还没有初始化完成
        try {
            if (cryptoObject == null) {
                cryptoObject = new BiometricPrompt.CryptoObject(new CipherHelper().createCipher());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 设置回调监听
     *
     * @param callback
     */
    @Override
    public void setFingerprintCallback(FingerprintCallback callback) {
        fingerprintCallback = callback;
    }

    /**
     * 设置弹窗样式
     *
     * @param verificationDialogStyle
     */
    @Override
    public void setVerificationDialogStyle(VerificationDialogStyle verificationDialogStyle) {
        this.verificationDialogStyle = verificationDialogStyle;
    }

    /**
     * 调起指纹验证
     * @param context
     */
    @Override
    public void authenticate(Context context) {
        if (verificationDialogStyle == null) {
            return;
        }
        /*
         * 初始化 BiometricPrompt.Builder
         */
        String title = TextUtils.isEmpty(verificationDialogStyle.getTitle()) ?
                context.getString(R.string.biometricprompt_fingerprint_verification) :
                verificationDialogStyle.getTitle();
        String cancelText = TextUtils.isEmpty(verificationDialogStyle.getCancelBtnText()) ?
                context.getString(R.string.biometricprompt_cancel) :
                verificationDialogStyle.getCancelBtnText();
        BiometricPrompt.Builder builder = new BiometricPrompt.Builder(context)
                .setTitle(title)
                .setNegativeButton(cancelText, new Executor() {
                    @Override
                    public void execute(Runnable command) {

                    }
                }, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {

                    }
                });
        if (!TextUtils.isEmpty(verificationDialogStyle.getSubTitle())) {
            builder.setSubtitle(verificationDialogStyle.getSubTitle());
        }
        if (!TextUtils.isEmpty(verificationDialogStyle.getDescription())) {
            builder.setDescription(verificationDialogStyle.getDescription());
        }

        // 构建 BiometricPrompt
        BiometricPrompt biometricPrompt = builder.build();

        // 取消扫描，每次取消后需要重新创建新示例
        cancellationSignal = new CancellationSignal();
        cancellationSignal.setOnCancelListener(new CancellationSignal.OnCancelListener() {
            @Override
            public void onCancel() {
                onDestroy();
            }
        });
        /*
         * 拉起指纹验证模块，等待验证
         * Executor：
         * context.getMainExecutor()
         */
        biometricPrompt.authenticate(cryptoObject, cancellationSignal, context.getMainExecutor(), authenticationCallback);
    }

    /**
     * 在 Android Q，Google 提供了 Api BiometricManager.canAuthenticate() 用来检测指纹识别硬件是否可用及是否添加指纹
     * 不过尚未开放，标记为"Stub"(存根)
     * 所以暂时还是需要使用 Andorid 6.0 的 Api 进行判断
     */
    @Override
    public boolean canAuthenticate(Context context) {
        /**
         * 硬件是否支持指纹识别
         */
        if (!FingerprintManagerCompat.from(context).isHardwareDetected()) {
            if (fingerprintCallback != null) {
                fingerprintCallback.onHwUnavailable();
            }
            onDestroy();
            return false;
        }
        /**
         * 是否已添加指纹
         */
        if (!FingerprintManagerCompat.from(context).hasEnrolledFingerprints()) {
            if (fingerprintCallback != null) {
                fingerprintCallback.onNoneEnrolled();
            }
            onDestroy();
            return false;
        }
        return true;
    }

    @Override
    public void cancel() {

    }

    @Override
    public void onDestroy() {
        fingerprintCallback = null;
        cancellationSignal = null;
        verificationDialogStyle = null;
    }
}